﻿using System;
using System.ServiceModel;
using WcfContrib.Helpers;

namespace WcfContrib.Hosting
{
	#region Enums
    /// <summary>
    /// Represents known endpoints configurations
    /// </summary>
	public enum KnownEndpointConfiguration
	{
        /// <summary>
        /// A default net-tcp configuration
        /// </summary>
		NetTcp,
        /// <summary>
        /// A custom net-tcp protocol with one-way binding element configuration for contracts with only O/W operations
        /// </summary>
		NetTcpOneWay,
        /// <summary>
        /// A net-tcp with reliable session enabled configuration
        /// </summary>
		NetTcpReliableSession,
        /// <summary>
        /// A default basic-http configuration
        /// </summary>
		BasicHttp,
        /// <summary>
        /// A default ws-http configuration
        /// </summary>
		WsHttp,
        /// <summary>
        /// A default named-pipe configuration
        /// </summary>
		NetNamedPipe,
        /// <summary>
        /// A default net-msmq configuration
        /// </summary>
		NetMsmq,
        /// <summary>
        /// A default web-http configuration
        /// </summary>
        WebHttp,
        /// <summary>
        /// A web-http configuration with script enabled for ajax-enabled
        /// </summary>
        WebHttpScriptEnabled,
        /// <summary>
        /// A http transport with binary message encoding
        /// </summary>
        HttpBinary
	}

    /// <summary>
    /// Represents known security modes
    /// </summary>
	public enum KnownSecurityMode
	{
        /// <summary>
        /// No security
        /// </summary>
		None,
        /// <summary>
        /// Windows client credential
        /// </summary>
		Windows
	}

    /// <summary>
    /// Represents metadata options
    /// </summary>
	[Flags]
	public enum MetadataOptions
	{
        /// <summary>
        /// No metadata
        /// </summary>
		None = 1,
        /// <summary>
        /// Http-get enabled
        /// </summary>
		HttpGet = 2,
        /// <summary>
        /// Metadata exchange through HTTP
        /// </summary>
		MetadataExchangeHttp = 4,
        /// <summary>
        /// Metadata exchange through net-tcp
        /// </summary>
		MetadataExchangeTcp = 8,
	}

    /// <summary>
    /// Represents communication protocols
    /// </summary>
	public enum Protocol
	{
        /// <summary>
        /// Http protocol
        /// </summary>
		Http,
        /// <summary>
        /// Net-tcp protocol
        /// </summary>
		NetTcp,
        /// <summary>
        /// Named-pipe protocol
        /// </summary>
		NetNamedPipe,
        /// <summary>
        /// Net-msmq protocol
        /// </summary>
		NetMsmq
	}
	#endregion

	#region Endpoints
    /// <summary>
    /// Represents endpoint configuration description
    /// </summary>
	public class EndpointConfigurationDescription
	{
        /// <summary>
        /// Gets or sets the known configuration.
        /// </summary>
        /// <value>The known configuration.</value>
		public KnownEndpointConfiguration KnownConfiguration { get; set; }
        /// <summary>
        /// Gets or sets the security mode.
        /// </summary>
        /// <value>The security mode.</value>
		public KnownSecurityMode SecurityMode { get; set; }
        /// <summary>
        /// Gets or sets the protocol.
        /// </summary>
        /// <value>The protocol.</value>
		public Protocol Protocol { get; set; }
        /// <summary>
        /// Gets or sets the address suffix.
        /// </summary>
        /// <value>The address suffix.</value>
		public string AddressSuffix { get; set; }
        /// <summary>
        /// Gets or sets the transaction flow.
        /// </summary>
        /// <value>The transaction flow.</value>
        public bool? TransactionFlow { get; set; }
        /// <summary>
        /// Gets or sets the desired port.
        /// </summary>
        /// <value>The port.</value>
        public int? Port { get; set; }

        /// <summary>
        /// Gets or sets the specific contract types supported by the endpoint.
        /// </summary>
        /// <value>The contract types.</value>
        public Type[] ContractTypes { get; set; }
	}

    /// <summary>
    /// Represents msmq endpoint configuration description
    /// </summary>
	public class MsmqEndpointConfigurationDescription : EndpointConfigurationDescription
	{
        /// <summary>
        /// Gets or sets the msmq endpoint details.
        /// </summary>
        /// <value>The msmq endpoint details.</value>
		public MsmqEndpointDetails Details { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MsmqEndpointConfigurationDescription"/> class.
        /// </summary>
		public MsmqEndpointConfigurationDescription()
			: this(new MsmqEndpointDetails())
		{
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="MsmqEndpointConfigurationDescription"/> class.
        /// </summary>
        /// <param name="details">The msmq endpoint details.</param>
		public MsmqEndpointConfigurationDescription(MsmqEndpointDetails details)
		{
			Details = details;
		}
	}

    /// <summary>
    /// Represents msmq endpoint details
    /// </summary>
	public class MsmqEndpointDetails
	{
        /// <summary>
        /// Gets or sets the time to live.
        /// </summary>
        /// <value>The time to live.</value>
		public TimeSpan TimeToLive { get; set; }
        /// <summary>
        /// Gets or sets the receive error handling.
        /// </summary>
        /// <value>The receive error handling.</value>
		public ReceiveErrorHandling ReceiveErrorHandling { get; set; }
        /// <summary>
        /// Gets or sets the receive retry count.
        /// </summary>
        /// <value>The receive retry count.</value>
		public int ReceiveRetryCount { get; set; }
        /// <summary>
        /// Gets or sets the max retry cycles.
        /// </summary>
        /// <value>The max retry cycles.</value>
		public int MaxRetryCycles { get; set; }
        /// <summary>
        /// Gets or sets the retry cycle delay.
        /// </summary>
        /// <value>The retry cycle delay.</value>
		public TimeSpan RetryCycleDelay { get; set; }
        /// <summary>
        /// Gets or sets a value indicating whether should process messages exactly once.
        /// </summary>
        /// <value><c>true</c> if should process messages exactly once; otherwise, <c>false</c>.</value>
		public bool ExactlyOnce { get; set; }
        /// <summary>
        /// Gets or sets a value indicating whether messages are durable.
        /// </summary>
        /// <value><c>true</c> if messages are durable; otherwise, <c>false</c>.</value>
		public bool Durable { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MsmqEndpointDetails"/> class.
        /// </summary>
		public MsmqEndpointDetails()
		{
			//netMsmq defaults
			Durable = true;
			ExactlyOnce = true;
			ReceiveRetryCount = 5;
			MaxRetryCycles = 2;
			RetryCycleDelay = TimeSpan.FromMinutes(30);
			TimeToLive = TimeSpan.FromDays(1);
		}
	}
	#endregion

	#region Protocols
    /// <summary>
    /// Represents protocol configuration description
    /// </summary>
	public class ProtocolConfigurationDescription
	{
        /// <summary>
        /// Gets the protocol description.
        /// </summary>
        /// <value>The protocol description.</value>
		public ProtocolDescription ProtocolDescription { get; private set; }
        /// <summary>
        /// Gets or sets the port.
        /// </summary>
        /// <value>The port.</value>
		public int Port { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProtocolConfigurationDescription"/> class.
        /// </summary>
        /// <param name="protocolDescription">The protocol description.</param>
		public ProtocolConfigurationDescription(ProtocolDescription protocolDescription)
		{
			ProtocolDescription = protocolDescription;
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="ProtocolConfigurationDescription"/> class.
        /// </summary>
        /// <param name="protocolDescription">The protocol description.</param>
        /// <param name="port">The port.</param>
		public ProtocolConfigurationDescription(ProtocolDescription protocolDescription, int port)
			: this(protocolDescription)
		{
			Port = port;
		}
	}

    /// <summary>
    /// Represents protocol description
    /// </summary>
	public abstract class ProtocolDescription
	{
        /// <summary>
        /// Represents a net-tcp protocol description
        /// </summary>
		public static readonly ProtocolDescription NetTcp = new NetTcpProtocolDescription();
        /// <summary>
        /// Represents a HTTP protocol description
        /// </summary>
		public static readonly ProtocolDescription Http = new HttpProtocolDescription();
        /// <summary>
        /// Represents a named-pipe protocol description
        /// </summary>
		public static readonly ProtocolDescription NetNamedPipe = new NetNamedPipeProtocolDescription();
        /// <summary>
        /// Represents a net-msmq protocol description
        /// </summary>
		public static readonly ProtocolDescription NetMsmq = new NetMsmqProtocolDescription();

        /// <summary>
        /// Gets the protocol.
        /// </summary>
        /// <value>The protocol.</value>
		public Protocol Protocol { get; private set; }
        /// <summary>
        /// Gets the scheme.
        /// </summary>
        /// <value>The scheme.</value>
		public string Scheme { get; private set; }
        /// <summary>
        /// Gets a value indicating whether to use local address.
        /// </summary>
        /// <value><c>true</c> if to use local address; otherwise, <c>false</c>.</value>
		public bool UseLocalAddress { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProtocolDescription"/> class.
        /// </summary>
        /// <param name="protocol">The protocol.</param>
        /// <param name="scheme">The scheme.</param>
        /// <param name="useLocalAddress">if set to <c>true</c> then use local address.</param>
		public ProtocolDescription(Protocol protocol, string scheme, bool useLocalAddress)
		{
			Protocol = protocol;
			Scheme = scheme;
			UseLocalAddress = useLocalAddress;
		}

        /// <summary>
        /// Handles the base address.
        /// </summary>
        /// <param name="address">The base address.</param>
        /// <returns></returns>
		public virtual string HandleBaseAddress(string address)
		{
			return address;
		}
	}

    /// <summary>
    /// Represents net-tcp protocol description
    /// </summary>
	public sealed class NetTcpProtocolDescription : ProtocolDescription
	{
        /// <summary>
        /// Initializes a new instance of the <see cref="NetTcpProtocolDescription"/> class.
        /// </summary>
		public NetTcpProtocolDescription()
			: base(Protocol.NetTcp, "net.tcp", false)
		{
		}
	}

    /// <summary>
    /// Represents named-pipe protocol description
    /// </summary>
	public sealed class NetNamedPipeProtocolDescription : ProtocolDescription
	{
        /// <summary>
        /// Initializes a new instance of the <see cref="NetNamedPipeProtocolDescription"/> class.
        /// </summary>
		public NetNamedPipeProtocolDescription()
			: base(Protocol.NetNamedPipe, "net.pipe", true)
		{
		}
	}

    /// <summary>
    /// Represents net-msmq protocol description
    /// </summary>
	public sealed class NetMsmqProtocolDescription : ProtocolDescription
	{
        /// <summary>
        /// Initializes a new instance of the <see cref="NetMsmqProtocolDescription"/> class.
        /// </summary>
		public NetMsmqProtocolDescription()
			: base(Protocol.NetMsmq, "net.msmq", true)
		{
		}

        /// <summary>
        /// Handles the base address.
        /// </summary>
        /// <param name="address">The base address.</param>
        /// <returns></returns>
		public override string HandleBaseAddress(string address)
		{
			return EndpointHelper.CombineAddresses(address, "private");
		}
	}

    /// <summary>
    /// Represents HTTP protocol description
    /// </summary>
	public sealed class HttpProtocolDescription : ProtocolDescription
	{
        /// <summary>
        /// Initializes a new instance of the <see cref="HttpProtocolDescription"/> class.
        /// </summary>
		public HttpProtocolDescription()
			: base(Protocol.Http, "http", false)
		{
		}
	}
	#endregion
}
